#ifndef __ADDRESS_H__
#define __ADDRESS_H__

#include <map>
#include <memory.h>
#include <memory>
#include <string>
#include <vector>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/un.h>
#include <stdint.h>

class IPAddress;

//地址基类
class Address
{
public:
    using ptr = std::shared_ptr<Address>;

    //通过sockaddr创建Address
    //addr:    sockaddr指针  
    //addrlen: aockaddr的长度
    //返回和socketaddr相匹配的Address，失败返回nullptr
    static Address::ptr Create(const sockaddr* addr, socklen_t addrlen);


    //通过host地址返回对应条件的所有Address
    //result   保存满足条件的Address
    //host     域名，服务器名等 例：www.baidu.top[:80] (方括号为可选内容)
    //family   协议族(AF_INT, AF_INT6, AF_UNIX)
    //type     socket类型，SOCK_STREAM、SOCK_DGRAM 等
    //protocol 协议,IPPROTO_TCP、IPPROTO_UDP 等
    //return： 返回是否转换成功
    static bool Lookup(std::vector<Address::ptr>& result, const std::string& host,
        int family = AF_INET, int type = SOCK_STREAM, int protocol = 0);


    //通过host地址返回对应条件的任意Address
    //host     域名，服务器名等 例：www.baidu.top[:80] (方括号为可选内容)
    //family   协议族(AF_INT, AF_INT6, AF_UNIX)
    //type     socket类型，SOCK_STREAM、SOCK_DGRAM 等
    //protocol 协议,IPPROTO_TCP、IPPROTO_UDP 等
    //return： 返回满足条件的任意Address,失败返回nullptr
    static Address::ptr LookupAny(const std::string& host,
        int family = AF_INET, int type = 0, int protocol = 0);


    //通过host地址返回对应条件的任意Address
    //host     域名，服务器名等 例：www.baidu.top[:80] (方括号为可选内容)
    //family   协议族(AF_INT, AF_INT6, AF_UNIX)
    //type     socket类型，SOCK_STREAM、SOCK_DGRAM 等
    //protocol 协议,IPPROTO_TCP、IPPROTO_UDP 等
    //return： 回满足条件的任意IPAddress,失败返回nullptr
    static std::shared_ptr<IPAddress> LookupAnyIPAddress(const std::string& host,
        int family = AF_INET, int type = 0, int protocol = 0);


    //brief  返回本机所有网卡的<网卡名, 地址, 子网掩码位数>
    //result 保存指定网卡所有地址
    //family 协议族(AF_INT, AF_INT6, AF_UNIX)
    //return 是否获取成功
    static bool GetInterfaceAddresses(std::multimap<std::string, 
        std::pair<Address::ptr, uint32_t>>& result, int family = AF_INET);


    //brief  获取指定网卡的地址和子网掩码位数
    //result 保存指定网卡所有地址
    //iface  网卡名称
    //family 协议族(AF_INT, AF_INT6, AF_UNIX)
    //return 是否获取成功
    static bool GetInterfaceAddresses(std::vector<std::pair<Address::ptr, uint32_t> >&result
                    ,const std::string& iface, int family = AF_INET);


    //返回协议族
    int getFamily() const;

    //返回字符串类型地址
    std::string toString() const;

    //返回sockaddr指针 （只读）
    virtual const sockaddr* getAddr() const = 0;

    //返回sockaddr指针 （读写）
    virtual sockaddr* getAddr() = 0;

    //返回sockaddr的长度
    virtual socklen_t getAddrLen() const = 0;

    //返回字符串类型地址
    virtual std::ostream& insert(std::ostream& os) const = 0;

    //比较函数
    bool operator<(const Address&) const;
    bool operator==(const Address&) const;
    bool operator!=(const Address&) const;
};


//IP地址基类
class IPAddress: public Address
{
public:
    using ptr = std::shared_ptr<IPAddress>;

    //brief   通过域名,IP,服务器名创建IPAddress
    //address 域名,IP,服务器名等
    //port    端口号
    //return  调用成功返回IPAddress,失败返回nullptr
    static IPAddress::ptr Create(const char* address);

    //返回端口号
    virtual uint32_t getPort() const = 0;

    //设置端口号
    virtual void setPort(uint16_t v) = 0;


    //brief      获取该地址的广播地址
    //prefix_len 子网掩码位数
    //调用成功返回IPAddress,失败返回nullptr
    virtual IPAddress::ptr broadcastAddress(uint32_t prefix_len) = 0;

    //brief      获取该地址的网段
    //prefix_len 子网掩码位数
    //调用成功返回IPAddress,失败返回nullptr
    virtual IPAddress::ptr networdAddress(uint prefix_len) = 0;

    //brief      获取子网掩码地址
    //prefix_len 子网掩码位数
    //调用成功返回IPAddress,失败返回nullptr
    virtual IPAddress::ptr subnetMask(uint32_t prefix_len) = 0;
};

//IPv4地址
class IPv4Address: public IPAddress
{
public:
    using ptr = std::shared_ptr<IPv4Address>;

    //使用点分十进制地址创建IPv4Address
    //address 点分十进制地址,如:192.168.1.1
    //port 端口号
    //return 返回IPv4Address,失败返回nullptr
    static IPv4Address::ptr Create(const char* address, uint16_t port = 0);

    //通过sockaddr_in构造IPv4Address
    //address sockaddr_in结构体
    IPv4Address(const sockaddr_in& address);

    //通过二进制地址构造IPv4Address
    //address 二进制地址address
    //port 端口号
    IPv4Address(uint32_t address = INADDR_ANY, uint16_t port = 0);

    //返回sockaddr_in指针,只读
    const sockaddr* getAddr() const override;
    //返回sockaddr_in指针,读写
    sockaddr* getAddr() override;
    //返回sockaddr的长度
    socklen_t getAddrLen() const override;
    //返回端口号
    uint32_t getPort() const override;
    //设置端口号
    void setPort(uint16_t v) override;
    //可读性输出地址
    std::ostream& insert(std::ostream& os) const override;
    
    //获取该地址的广播地址
    IPAddress::ptr broadcastAddress(uint32_t prefix_len) override;
    //获取该地址的网段
    IPAddress::ptr networdAddress(uint32_t prefix_len) override;
    //获取子网掩码地址
    IPAddress::ptr subnetMask(uint32_t prefix_len) override;

private:
    sockaddr_in m_addr;
};

//IPv6地址
class IPv6Address: public IPAddress
{
public:
    using ptr = std::shared_ptr<IPv6Address>;

    //通过IPv6地址字符串构造IPv6Address
    //address IPv6地址字符串
    //port 端口号
    //return 返回IPv6Address,失败返回nullptr
    static IPv6Address::ptr Create(const char* address, uint16_t port = 0);

    //无参构造
    IPv6Address();

    //通过sockaddr_in6构造IPv6Address
    //address sockaddr_in6结构体
    IPv6Address(const sockaddr_in6& address);

    //通过IPv6二进制地址构造IPv6Address
    //address 二进制地址address
    //port 端口号
    IPv6Address(const uint8_t address[16], uint16_t port = 0);


    //返回sockaddr_in6指针,只读
    const sockaddr* getAddr() const override;
    //返回sockaddr_in6指针,读写
    sockaddr* getAddr() override;
    //返回sockaddr的长度
    socklen_t getAddrLen() const override;
    //返回端口号
    uint32_t getPort() const override;
    //设置端口号
    void setPort(uint16_t v) override;
    //可读性输出地址
    std::ostream& insert(std::ostream& os) const override;

    //获取该地址的广播地址
    IPAddress::ptr broadcastAddress(uint32_t prefix_len) override;
    //获取该地址的网段
    IPAddress::ptr networdAddress(uint32_t prefix_len) override;
    //获取子网掩码地址
    IPAddress::ptr subnetMask(uint32_t prefix_len) override;

private:
    sockaddr_in6 m_addr;
};


//unixaddr 本地套接字
class UnixAddress: public Address
{
public:
    using ptr = std::shared_ptr<UnixAddress>;

    UnixAddress();

    //通过路径构造UnixAddress
    //path UnixSocket路径(长度小于UNIX_PATH_MAX)
    UnixAddress(const std::string& path);

    void setAddrLen(uint32_t v);
    std::string getPath() const;

    //返回sockaddr指针,只读
    const sockaddr* getAddr() const override;
    //返回sockaddr指针,读写
    sockaddr* getAddr() override;
    //返回sockaddr的长度
    socklen_t getAddrLen() const override;
    //可读性输出地址
    std::ostream& insert(std::ostream& os) const override;
private:
    sockaddr_un m_addr;
    socklen_t m_length;     //sockaddr_un有数据的长度（sizeof(m_addr.sun_family) + m_addr.sun_path.size())
};


//未知地址
class UnknownAddress: public Address
{
public:
    using ptr = std::shared_ptr<UnknownAddress>;

    UnknownAddress(int family);
    UnknownAddress(const sockaddr& addr);

    //返回sockaddr指针,只读
    const sockaddr* getAddr() const override;
    //返回sockaddr指针,读写
    sockaddr* getAddr() override;
    //返回sockaddr的长度
    socklen_t getAddrLen() const override;
    //可读性输出地址
    std::ostream& insert(std::ostream& os) const override;
private:
    sockaddr m_addr;
};


//流式输出Address
std::ostream& operator<<(std::ostream& os, const Address& addr);

#endif //__ADDRESS_H__
